﻿using MySqlConnector;
using ThresholdAssistant.Dapper.Enums;
using ThresholdAssistant.DataContract.Attributes;
using ThresholdAssistant.Tools.Extensions;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data;
using System.Linq;
using System.Reflection;

namespace ThresholdAssistant.Dapper.Extensions
{
    /// <summary>
    /// Dapper扩展方法
    /// </summary>
    public static class DapperExtensions
    {
        /// <summary>
        /// 列特性类型
        /// </summary>
        private static readonly Type attributeType = typeof(ColumnAttribute);

        /// <summary>
        /// 批量新增
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dataLit"></param>
        /// <param name="connectionString">数据库连接字符串</param>
        /// <param name="dbType">数据库类型</param>
        /// <param name="tableName">表名(默认不填，使用TableName特性标记)</param>
        public static void BatchAdd<T>(this IEnumerable<T> dataLit, string connectionString, DbTypeEnum dbType, string tableName = "")
        {
            tableName = GetTableName<T>(tableName);
            DataTable dataTable = new DataTable(tableName);
            PropertyInfo[] propertyInfoList = typeof(T).GetProperties().OrderBy(e => e.GetCustomAttribute<OrderAttribute>()?.Order ?? 0).ToArray();
            foreach (var propertyInfo in propertyInfoList)
            {
                Type propertyType = GetPropertyType(propertyInfo);//获取属性类型
                var columnName = GetColumnName(propertyInfo);//获取列明
                dataTable.Columns.Add(columnName, propertyType);//设置列明
            }

            foreach (T dataInfo in dataLit)
            {
                DataRow dataRow = dataTable.NewRow();
                foreach (var propertyInfo in propertyInfoList)
                {
                    Type propertyType = GetPropertyType(propertyInfo);//获取属性类型
                    var columnName = GetColumnName(propertyInfo);//获取列明
                    object value = propertyInfo.GetValue(dataInfo, null);
                    if (value == null)
                    {
                        dataRow[columnName] = DBNull.Value;
                    }
                    else
                    {
                        if (propertyType.IsEnum)
                        {
                            //处理枚举类型
                            dataRow[columnName] = (int)value;
                        }
                        else
                        {
                            dataRow[columnName] = value;
                        }
                    }
                }
                dataTable.Rows.Add(dataRow);
            }

            switch (dbType)
            {
                case DbTypeEnum.MsSql:
                    BatchAddToMsSql(dataTable, connectionString);
                    break;
                case DbTypeEnum.MySql:
                    BatchAddToMySql(dataTable, connectionString);
                    break;
            }
        }


        /// <summary>
        /// 获取列名称
        /// </summary>
        /// <param name="propertyInfo">属性</param>
        private static string GetColumnName(PropertyInfo propertyInfo)
        {
            ColumnAttribute columnAttribute = null;
            object[] columnAttrList = propertyInfo.GetCustomAttributes(attributeType, true);
            if (columnAttrList != null && columnAttrList.Any())
            {
                columnAttribute = columnAttrList.FirstOrDefault() as ColumnAttribute;
            }

            var columnName = propertyInfo.Name;
            if (columnAttribute != null)
            {
                //绑定列明
                columnName = columnAttribute.Name;
            }
            return columnName;
        }

        /// <summary>
        /// 获取属性类型
        /// </summary>
        /// <param name="propertyInfo"></param>
        /// <returns></returns>
        private static Type GetPropertyType(PropertyInfo propertyInfo)
        {
            Type propertyType = propertyInfo.PropertyType;
            if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
            {
                Type[] types = propertyInfo.PropertyType.GetGenericArguments();
                propertyType = types.FirstOrDefault();
            }
            return propertyType;
        }

        /// <summary>
        /// 批量插入数据表
        /// </summary>
        /// <param name="dataTalbe">要批量插入到数据库中的数据表</param>
        private static void BatchAddToMsSql(DataTable dataTalbe, string connectionString)
        {
            using System.Data.SqlClient.SqlConnection destinationConnection = new System.Data.SqlClient.SqlConnection(connectionString);
            if (destinationConnection.State != ConnectionState.Open)
            {
                destinationConnection.Open();
            }
            using (System.Data.SqlClient.SqlBulkCopy bulkCopy = new System.Data.SqlClient.SqlBulkCopy(destinationConnection))
            {
                bulkCopy.BatchSize = 1000;
                bulkCopy.DestinationTableName = dataTalbe.TableName;
                bulkCopy.WriteToServer(dataTalbe);
            }
            destinationConnection.Close();
        }

        /// <summary>
        /// 批量插入数据表
        /// </summary>
        /// <param name="datatable">要批量插入到数据库中的数据表</param>
        /// <param name="connectionString">数据库连接字符串</param>
        public static void BatchAddToMySql(this DataTable datatable, string connectionString)
        {
            connectionString = $"{connectionString.TrimEnd(';')};AllowLoadLocalInfile = true;";
            using MySqlConnection connection = new MySqlConnection(connectionString);
            if (connection.State != ConnectionState.Open)
            {
                connection.Open();
            }
            var bulkCopy = new MySqlBulkCopy(connection);
            bulkCopy.DestinationTableName = datatable.TableName;
            bulkCopy.WriteToServer(datatable);
            connection.Close();
        }

        /// <summary>
        /// 获取表名称
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="tableName"></param>
        /// <returns></returns>
        private static string GetTableName<T>(string tableName)
        {
            if (!string.IsNullOrEmpty(tableName))
                return tableName;

            //如果没传入表明的情况下使用默认表明
            return typeof(T).GetAttributeValue((TableAttribute tab) => tab.Name);
        }
    }
}